#pragma once
#include "yzrutil.h"
#include "lang/Exception.h"
#include "lang/String.h"
#include "lang/StringBuilder.h"
#include "lang/StringFormat.hpp"
#include "collection/Iterable.hpp"
#include "array/Array.hpp"

namespace yzrilyzr_collection{
	template<typename E>
	ECLASS(Collection, public Iterable<E>){
	public:
	virtual ~Collection()=default;
	U_NO_DISCARD virtual u_index size() const=0;
	virtual bool isEmpty()const{
		return size() == 0;
	}

	virtual bool contains(const E & o) const{
		for(E & e : *this){
			if(o == e)return true;
		}
		return false;
	}

	virtual yzrilyzr_array::Array<E> toArray() const{
		auto r=yzrilyzr_array::Array<E>(size());
		u_index i=0;
		for(E & e : *this){
			r[i]=e;
			i++;
		}
		return r;
	}

	virtual bool add(const E & e)=0;

	virtual bool remove(const E & e){
		std::unique_ptr<Iterator<E>> itp=this->iterator();
		auto & it=*itp;
		while(it.hasNext()){
			if(e == it.next()){
				it.remove();
				return true;
			}
		}
		return false;
	}

	virtual bool containsAll(const Collection<E> &c) const{
		for(E & e : c)
			if(!contains(e))
				return false;
		return true;
	}

	virtual bool addAll(const Collection<E> &c){
		bool modified=false;
		for(E &e : c)
			if(add(e))
				modified=true;
		return modified;
	}

	virtual bool removeAll(const Collection<E> &c){
		bool modified=false;
		std::unique_ptr<Iterator<E>> itp=this->iterator();
		auto & it=*itp;
		while(it.hasNext()){
			if(c.contains(it.next())){
				it.remove();
				modified=true;
			}
		}
		return modified;
	}

	virtual bool retainAll(const Collection<E> &c){
		bool modified=false;
		std::unique_ptr<Iterator<E>> itp=this->iterator();
		auto & it=*itp;
		while(it.hasNext()){
			if(!c.contains(it.next())){
				it.remove();
				modified=true;
			}
		}
		return modified;
	}

	virtual void clear(){
		std::unique_ptr<Iterator<E>> itp=this->iterator();
		auto & it=*itp;
		while(it.hasNext()){
			it.next();
			it.remove();
		}
	}

	yzrilyzr_lang::String toString() const override{
		if(isEmpty())return "[]";
		yzrilyzr_lang::StringBuilder sb;
		sb.append("[");
		for(E &e : *this){
			sb.append(yzrilyzr_lang::ObjectUtil::toString(e));
			sb.append(',').append(' ');
		}
		return sb.append(']');
	}

	bool empty(){
		return isEmpty();
	}

	void push_back(const E & e){
		add(e);
	}

	void erase(const E & e){
		remove(e);
	}
	};
}